package org.examples.java.lang;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Console;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.channels.Channel;
import java.util.Map;
import java.util.Properties;

import org.junit.Test;

public class SystemTest {

    @Test
    public void testConstructor() throws Exception
    {
        assertNull(System.class.getClassLoader());
    }

    @Test
    public void testArrayCopy() throws Exception
    {
        String[] src = new String[] { "AAA", "BBB" };
        Object[] dest = new Object[2];
        System.arraycopy(src, 0, dest, 0, 2);
        assertEquals(src[0], dest[0]);
        assertEquals(src[1], dest[1]);

        System.arraycopy(dest, 0, src, 0, 2);
        assertEquals(src[0], dest[0]);
        assertEquals(src[1], dest[1]);
    }

    @Test(expected = ArrayStoreException.class)
    public void testArrayCopyFail() throws Exception
    {
        String[] src = new String[] { "AAA", "BBB" };
        Integer[] dest = new Integer[2];
        System.arraycopy(src, 0, dest, 0, 2);
    }

    @Test
    public void testGetSetProperties() throws Exception
    {
        int originalSize = System.getProperties().size();
        assertEquals(null, System.getProperty("propertyXXX"));
        System.setProperty("propertyXXX", "-VALUE-");
        assertEquals(System.getProperties().size(), originalSize + 1);
        assertEquals("-VALUE-", System.getProperty("propertyXXX"));

        System.clearProperty("propertyXXX");
        assertEquals(null, System.getProperty("propertyXXX"));

        assertEquals("XXX", System.getProperty("propertyXXX", "XXX"));

        // direct references to system properties
        Properties properties = System.getProperties();
        properties.put("newProperty", "newValue");
        assertEquals("newValue", System.getProperty("newProperty"));

        // use setter to assign change system properties
        Properties properties2 = new Properties(System.getProperties());
        properties2.put("newProperty2", "newValue2");
        assertEquals(null, System.getProperty("newProperty2"));
        System.setProperties(properties2);
        assertEquals("newValue2", System.getProperty("newProperty2"));
    }

    @Test
    public void testConsole()
    {
        Console console = System.console();
        assertNull(console);
    }

    @Test
    public void testIdentityHashCode() {
        Object obj = new Object() {
            public int hashCode() {
                return 10;
            }
        };
        assertEquals(10, obj.hashCode());
        assertFalse(10 == System.identityHashCode(obj));
    }

    @Test
    public void testInheritedChannel() throws IOException {
        Channel inheritedChannel = System.inheritedChannel();
        assertNull(inheritedChannel);
    }

    @Test
    public void testGetEnv() {
        Map<String, String> envs = System.getenv();
        assertTrue(envs.size() > 0);
        assertNull(System.getenv("XXXXXX"));
    }

    @Test
    public void testGetTime() {
        long currentTimeMillis = System.currentTimeMillis();
        long currentTimeMillis2 = System.currentTimeMillis();
        // on modern processors it's VERY likely that this two values will be equal
        assertEquals(currentTimeMillis2, currentTimeMillis);

        long startTime = System.nanoTime();
        for(int i = 0; i < 10; i++)
            ;
        long estimatedTime = System.nanoTime() - startTime;
        startTime = System.nanoTime();
        for(int i = 0; i < 100; i++)
            ;
        long estimatedTime2 = System.nanoTime() - startTime;
        // System.out.println("for(int i = 0; i < 10; i++) took: " + estimatedTime
        // + ", for(int i = 0; i < 100; i++) took: " + estimatedTime2);
        assertTrue(estimatedTime2 > estimatedTime);
    }

    //@Test
    public void testLoad() {
    	String libName;
        if (System.getProperty("os.name").toLowerCase().contains("win"))
        	libName =  "swt-win32-3735.dll";
        else
        	libName = "libply-boot-client.so.2";

        	System.load(System.getProperty("user.dir") + "/src/test/resources/" + libName);
    }

    @Test
    public void testMapLibraryName() {
        String libraryName = System.mapLibraryName("aaa");
        if (System.getProperty("os.name").toLowerCase().contains("win"))
            assertEquals("aaa.dll", libraryName);
        else
            assertEquals("libaaa.so", libraryName);
    }

    @Test(expected = UnsatisfiedLinkError.class)
    public void testLoad_Failed() {
        System.load("aaaa");
    }

    @Test(expected = UnsatisfiedLinkError.class)
    public void testLoadLibrary() {
        System.loadLibrary("");
    }

    @Test
    public void testSetOutErr() throws IOException {
        PrintStream orginalOut = System.out;
        PrintStream orginalErr = System.err;
        InputStream originalIn = System.in;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            System.setOut(new PrintStream(out));
            System.setErr(new PrintStream(out));

            // test out streams
            System.out.print("1!");
            System.err.print("2!");
            assertEquals("1!2!", out.toString());

            // test in stream
            System.setIn(new ByteArrayInputStream(new byte[] { 9 }));
            int read = System.in.read();
            assertEquals(9, read);
        }
        finally {
            System.setOut(orginalOut);
            System.setErr(orginalErr);
            System.setIn(originalIn);
        }
    }

    @Test
    public void testGc() {
        // how to check?
        System.gc();
    }

    @Test
    public void testRunFinalyzation() {
        final Boolean[] invoked = new Boolean[] { Boolean.FALSE };
        makeAndLeaveDieFinalyzabeObject(invoked);

        System.gc();
        System.runFinalization();
        // on my home machine this always seems to be true. leave this code to check on other machines
        assertTrue("finalyze method was not invoked", invoked[0]);
    }

    private void makeAndLeaveDieFinalyzabeObject(final Boolean[] invoked) {
        new Object() {
            @Override
            protected void finalize() throws Throwable {
                invoked[0] = Boolean.TRUE;
                super.finalize();
            }
        };
    }

    @Test
    public void testGetSetSecurityManager() {
        SecurityManager securityManager = System.getSecurityManager();
        assertNull(securityManager);
        // method does nothing
        System.setSecurityManager(null);
    }
}
